﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using Dycox.ComponentModel;

namespace Dycox.Reporting
{
    public class Report
    {
        
        public Report(string name)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            _name = name;
            _body = new ReportBody(this);
            _pageSettings = new ReportPageSettings(this);
            _variables = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
        }

        private ReportPageSettings _pageSettings;

        public ReportPageSettings PageSettings
        {
            get { return _pageSettings; }
        }

        private ReportBody _body;

        public ReportBody Body
        {
            get { return _body; }
        }

        private ElementStyle _defaultStyle;

        public ElementStyle DefaultStyle
        {
            get
            {
                if (_defaultStyle == null)
                    _defaultStyle = ElementStyle.CreateDefaultStyle();
                return _defaultStyle;
            }
        }

        private Dictionary<string, object> _variables;

        string _name;
        [ReportVariable]
        public string Name
        {
            get
            {
                return _name;
            }
        }

        [ReportVariable]
        public DateTime Now
        {
            get
            {
                if (_timeService != null)
                    return _timeService.Now;
                else
                    return DateTime.Now;
            }
        }

        private string _description;
        [ReportVariable]
        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }


        public object GetVariable(string name)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            PropertyDescriptor vp = GetVariableProperty(name);
            if (vp != null)
                return vp.GetValue(this);

            object value;
            if (_variables.TryGetValue(name, out value))
            {
                return value;
            }
            else
            {
                RetrieveVariableEventArgs e = new RetrieveVariableEventArgs(name);
                OnRetrieveVariable(e);
                return e.Value;
            }
        }

        public event RetrieveVariableEventHandler RetrieveVariable;

        protected virtual void OnRetrieveVariable(RetrieveVariableEventArgs e)
        {
            if (RetrieveVariable != null)
                RetrieveVariable(this, e);
        }

        PropertyDescriptorCollection _properties;
        private PropertyDescriptor GetVariableProperty(string name)
        {
            if (_properties == null)
                _properties = TypeDescriptor.GetProperties(this, new Attribute[] { ReportVariableAttribute.Instance });

            return _properties[name];
        }

        public void SetVariable(string name, object value)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            PropertyDescriptor vp = GetVariableProperty(name);
            if (vp != null)
            {
                if (!vp.IsReadOnly)
                    vp.SetValue(this, value);
                else
                    throw new InvalidOperationException("Cannot assign built-in variables. " + name);
            }
            else
            {
                if (value == null)
                    _variables.Remove(name);
                else
                    _variables[name] = value;
            }
        }

        public void Render(Graphics graphics, object dataObject)
        {
            if (graphics == null)
                throw new ArgumentNullException("graphics");

            //renderer.BeginRender(this);
            //renderer.RenderContainer(_body);
            //renderer.EndRender();
            RenderingContext context = new RenderingContext(this, graphics);
            context.Render(dataObject, null);
        }

        private static ITimeService _timeService;

        public static ITimeService TimeService
        {
            get { return _timeService; }
            set { _timeService = value; }
        }

    }
}
